﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Net_SCADA
{
    public class hsl_Function
    {
        private static Dictionary<string, ushort> typestring = new Dictionary<string, ushort>()//需要查询的数据类型和字节长度
            {
                {"DINT",4 },
                {"INT",2 },
                {"DWORD",4 },
                {"W",2 },
                {"CHAR",1},
                {"B",1 },
                {"REAL",4 },
                {"X",1 },
                {"DT",8 },
                {"DATE",2 },
                {"TIME",4 },
            };

        #region opc的地址解析
        /// <summary>
        /// 返回解析是否成功
        /// </summary>
        /// <param name="add">需要解析的地址</param>
        /// <param name="groupadd">返回的分区地址</param>
        /// <param name="datatype">返回代表数据类型的字符串</param>
        /// <param name="byte_offset">返回字节的偏移值</param>
        /// <param name="bit_offset">返回位的偏移地址</param>
        /// <param name="byte_length">返回需要读取数据的长度</param>
        /// <returns>true表示成功，false表示失败</returns>
        public static bool OpcaddToHsladd(string add, out string groupadd, out string datatype, out int byte_offset, out int bit_offset, out int byte_length)
        {
            groupadd = "";
            datatype = "";
            byte_offset = 0;
            bit_offset = 0;
            byte_length = 0;
            string[] s = add.ToUpper().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            if (s.Length == 1)//没有逗号
            {
                if (s[0].Length < 3) return false;//不足3个的地址应该是不合理的至少是MB3
                groupadd = s[0].Substring(0, 1);//取第一个，M,Q,I
                s[0] = s[0].Substring(1).ToUpper();//去掉第一个,并使之全部大写
                return SubaddSplit(s[0], out datatype, out byte_offset, out bit_offset, out byte_length);
            }
            else//有逗号，拆分数据S[0]为地址，s[1]数据类型和偏移地址
            {
                groupadd = s[0];
                return SubaddSplit(s[1], out datatype, out byte_offset, out bit_offset, out byte_length);
            }
        }
        /// <summary>
        /// 返回解析是否成功
        /// </summary>
        /// <param name="add">需要解析的地址</param>
        /// <param name="datatype">返回代表数据类型的字符串</param>
        /// <param name="byte_offset">返回字节的偏移值</param>
        /// <param name="bit_offset">返回位的偏移地址</param>
        /// <param name="byte_length">返回需要读取数据的长度</param>
        /// <returns>true表示成功，false表示失败</returns>
        private static bool SubaddSplit(string add, out string datatype, out int byte_offset, out int bit_offset, out int byte_length)
        {

            datatype = "";
            byte_offset = 0;
            bit_offset = 0;
            byte_length = 0;
            string _add = string.Empty;
            foreach (var item in typestring.Keys)
            {
                if (add.IndexOf(item) >= 0)
                {
                    datatype = item;
                    byte_length = typestring[item];
                    _add = add.Replace(item, "");//将地址中的数据类型替换掉
                    break;
                }
            }
            if (datatype == "") return false;//没有检索到，返回false
            else//正常检索到数据类型
            {
                if (datatype == "X")//如果是位数据类型，进行位地址的解析
                {
                    string[] s = _add.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);//使用小数点拆分
                    if (s.Length == 1)//如果没有小数点，默认位地址是0
                    {
                        bit_offset = 0;
                        return int.TryParse(s[0], out byte_offset);//解析数据，并返回是否解析成功
                    }
                    else//有小数点，对小数点前后的数据进行解析
                    {
                        bool isok = int.TryParse(s[0], out byte_offset) && int.TryParse(s[1], out bit_offset);
                        if (bit_offset > 7) isok = false;//如果解析的位数据超过7解析失败
                        return isok;
                    }
                }
                else//其他数据类型，直接解析后面的数值并返回
                {
                    return int.TryParse(_add, out byte_offset);
                }
            }
        }

        #endregion

        #region 读取的数据解析
        public static object GetRealData(string datatype, byte[] bytedata,int offset, int bitoffset=0)
        {
            HslCommunication.Core.IByteTransform ByteTransform = new HslCommunication.Core.ReverseBytesTransform();

            object obj=null;

            if (typestring.Keys.Contains(datatype))//数据类型符合既有的类型
            {
                if (bytedata.Length < offset+ typestring[datatype])//数据长度不对
                    return obj;
                else
                {
                    
                    switch (datatype.ToUpper())
                    {
                        case "DINT":
                            obj=ByteTransform.TransInt32(bytedata, offset);
                            break;
                        case "INT":
                            obj=ByteTransform.TransInt16(bytedata, offset);
                            break;
                        case "DWORD":
                            obj =ByteTransform.TransUInt32(bytedata, offset);
                            break;
                        case "W":
                            obj=ByteTransform.TransUInt16(bytedata, offset);
                            break;
                        case "CHAR":
                            obj = Convert.ToChar(bytedata[offset]) ;
                            break;
                        case "B":
                            obj = bytedata[offset];
                            break;
                        case "REAL":
                            obj = ByteTransform.TransSingle(bytedata, offset);
                            break;
                        case "X":
                            if (bitoffset > 7) return obj;
                            StringBuilder sb = new StringBuilder(Convert.ToString(bytedata[offset], 2).PadLeft(8, '0'));
                            obj=(sb[7 - bitoffset]=='1'?"true":"false");
                            break;
                        case "DT"://在PLC内使用的是8个Byte，但是最后的两个用一个整形表示了毫秒数，只解析前面的6个byte
                            obj=new StringBuilder().Append(bytedata[offset] >= 144 ? "19" : "20")//年前两位
                                .Append(bytedata[offset].ToString("X2")).Append("-")//年的后两位
                                .Append(bytedata[offset+1].ToString("X2")).Append("-")//月
                                .Append(bytedata[offset+2].ToString("X2")).Append(" ")//日
                                .Append(bytedata[offset+3].ToString("X2")).Append(":")//时
                                .Append(bytedata[offset+4].ToString("X2")).Append(":")//分
                                .Append(bytedata[offset+5].ToString("X2")).ToString();//秒
                            break;
                        case "DATE":
                            DateTime dt = new DateTime(1990, 1, 1);
                            obj=dt.AddDays(ByteTransform.TransInt16(bytedata, offset)).ToString("yyyy-MM-dd");
                            break;
                        case "TIME":
                            obj=ByteTransform.TransInt32(bytedata, offset);//暂时还没有搞清楚是怎么存储的
                            break;
                        default:
                            break;
                    }
                }
            }
            return obj;
        }
        #endregion

        #region 写入的数据解析
        public static byte[] GetSetData(string datatype,object value)
        {
            byte[] res=new byte[0];
            if (typestring.Keys.Contains(datatype))
            {
                res = new byte[typestring[datatype]];
                switch (datatype.ToUpper())
                {
                    case "DINT":
                        if (int.TryParse(value.ToString(), out int vDint))
                        {
                            res = BitConverter.GetBytes(vDint);
                            ReserveArray(ref res);
                        }
                        else
                            return null;
                        break;
                    case "INT":
                        if (short.TryParse(value.ToString(), out short vInt))
                        {
                            res = BitConverter.GetBytes(vInt);
                            ReserveArray(ref res);
                        }
                        else
                            return null;
                        break;
                    case "DWORD":
                        if (uint.TryParse(value.ToString(), out uint vUInt))
                        {
                            res = BitConverter.GetBytes(vUInt);
                            ReserveArray(ref res);
                        }
                        else
                            return null;
                        break;
                    case "W":
                        if (ushort.TryParse(value.ToString(), out ushort vUShort))
                        {
                            res = BitConverter.GetBytes(vUShort);
                            ReserveArray(ref res);
                        }
                        else
                            return null;
                        break;
                    case "CHAR"://暂时按照byte处理
                    case "B":
                        if (byte.TryParse(value.ToString(), out byte vByte))
                        {
                            res[0] = vByte;
                        }
                        else
                            return null;
                        break;
                    case "REAL":
                        if (float.TryParse(value.ToString(), out float vFloat))
                        {
                            res = BitConverter.GetBytes(vFloat);
                            ReserveArray(ref res);
                        }
                        else
                            return null;
                        break;
                    case "DT":
                        if (DateTime.TryParse(value.ToString(), out DateTime vDatetime))
                        {
                            res[0] = ByteToBCD(vDatetime.Year % 100); //Convert.ToByte((vDatetime.Year % 100).ToString());
                            res[1] = ByteToBCD(vDatetime.Month);// Convert.ToByte((vDatetime.Month).ToString());
                            res[2] = ByteToBCD(vDatetime.Day);// Convert.ToByte((vDatetime.Day).ToString());
                            res[3] = ByteToBCD(vDatetime.Hour);// Convert.ToByte((vDatetime.Hour).ToString());
                            res[4] = ByteToBCD(vDatetime.Minute);// Convert.ToByte((vDatetime.Minute).ToString());
                            res[5] = ByteToBCD(vDatetime.Second);// Convert.ToByte((vDatetime.Second).ToString());
                        }
                        else
                            return null;
                        break;
                    case "DATE":
                        if (DateTime.TryParse(value.ToString(), out DateTime VDate))
                        {
                            DateTime dt = new DateTime(1990, 1, 1);
                            res = BitConverter.GetBytes((short)(VDate.Subtract(dt).TotalDays));
                            ReserveArray(ref res);
                        }
                        else
                            return null;
                        break;
                    case "TIME":
                        return null;
                    default:
                        return null;
                }
            }
            return res;
                
        }

        private static byte ByteToBCD(int v)
        {
            int a = (v/10) * 16 ;
            int b = v % 10;
            return (byte)(a + b);
        }
        public static void ReserveArray(ref byte[] arr)
        {
            int l = arr.Length;
            byte obj;
            for (int i = 0; i < l / 2; i++)
            {
                obj = arr[i];
                arr[i] = arr[l - 1 - i];
                arr[l - 1 - i] = obj;
            }
        }


        #endregion

    }
}
